// 
// SoapTrace.Net - A soap message monitoring utility. 
// 
// Copyright (C) 2004-2009 Matthew Ward
// 
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
// 
// Matthew Ward (mrward@users.sourceforge.net)
// 

using SoapTrace.Core.Communication;
using System;
using System.Diagnostics;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Globalization;
using System.Net;
using System.Windows.Forms;

namespace SoapTrace
{
	/// <summary>
	/// Form containing all the Soap Trace application's server settings.
	/// </summary>
	public partial class SettingsForm : Form
	{
		/// <summary>
		/// The network interface names on this machine.
		/// </summary>
		private NetworkInterfaceNameCollection m_NetworkInterfaceNames = new NetworkInterfaceNameCollection( );
		
		/// <summary>
		/// Network interfaces need to be retrieved event.
		/// </summary>
		public event EventHandler GetNetworkInterfaces;
		
		/// <summary>
		/// Turns on or off the processing of events when programmatically changing
		/// UI items that trigger events. 
		/// </summary>
		private bool m_ProcessEvents = true;
		
		/// <summary>
		/// Creates a new instance of this class.
		/// </summary>
		public SettingsForm( )
		{
			//
			// Required for Windows Form Designer support
			//
			InitializeComponent( );
		}

		/// <summary>
		/// Gets or sets the remote server's destination port.
		/// </summary>
		public int RemoteServerPort
		{
			[ DebuggerStepThrough ]
			get
			{
				return Convert.ToInt32( RemoteServerPortTextBox.Text, CultureInfo.InvariantCulture );
			}

			[ DebuggerStepThrough ]
			set
			{
				RemoteServerPortTextBox.Text = value.ToString( CultureInfo.InvariantCulture );
			}
		}

		/// <summary>
		/// Gets or sets the destination server's name or IP address.
		/// </summary>
		public string RemoteServerIPAddress
		{
			[ DebuggerStepThrough ]
			get
			{
				return RemoteServerTextBox.Text;
			}

			[ DebuggerStepThrough ]
			set
			{
				RemoteServerTextBox.Text = value;
			}
		}

		/// <summary>
		/// Gets or sets the port that the proxy server should listen on.
		/// </summary>
		public int ProxyServerListeningPort
		{
			[ DebuggerStepThrough ]
			get
			{
				return Convert.ToInt32( ListeningPortTextBox.Text, CultureInfo.InvariantCulture );
			}

			[ DebuggerStepThrough ]
			set
			{
				ListeningPortTextBox.Text = value.ToString( CultureInfo.InvariantCulture );
			}
		}

		/// <summary>
		/// Gets or sets the use proxy server setting.
		/// </summary>
		public bool UseProxyServer
		{
			[ DebuggerStepThrough ]
			get
			{
				return UseProxyServerRadioButton.Checked;
			}

			set
			{
				try
				{
					m_ProcessEvents = false;
					
					UseProxyServerRadioButton.Checked = value;
					ListeningPortTextBox.Enabled = value;
					
					if(value)
					{
						UseWinPcapRadioButton.Checked = false;
						UseRawSocketsRadioButton.Checked = false;
						NetworkInterfaceComboBox.Enabled = false;
					}
				}
				finally
				{
					m_ProcessEvents = true;
				}
			}
		}

		/// <summary>
		/// Gets or sets the use WinPcap packet capture setting.
		/// </summary>
		public bool UseWinPcap
		{
			[ DebuggerStepThrough ]
			get
			{
				return UseWinPcapRadioButton.Checked;
			}

			set
			{	
				try
				{		
					m_ProcessEvents = false;
					UseWinPcapRadioButton.Checked = value;
					
					if(value)
					{
						ListeningPortTextBox.Enabled = false;
						NetworkInterfaceComboBox.Enabled = true;
						UseProxyServerRadioButton.Checked = false;
					}
				}
				finally
				{
					m_ProcessEvents = true;
				}
			}
		}
		
		/// <summary>
		/// Gets or sets the use raw sockets packet capture setting.
		/// </summary>
		public bool UseRawSockets
		{
			[ DebuggerStepThrough ]
			get
			{
				return UseRawSocketsRadioButton.Checked;
			}

			set
			{
				try
				{
					m_ProcessEvents = false;
					UseRawSocketsRadioButton.Checked = value;
					
					if(value)
					{
						ListeningPortTextBox.Enabled = false;
						NetworkInterfaceComboBox.Enabled = true;
						UseProxyServerRadioButton.Checked = false;
					}
				}
				finally
				{
					m_ProcessEvents = true;
				}
			}
		}		

		/// <summary>
		/// Sets the network interface names to be displayed in the
		/// combo box.
		/// </summary>
		/// <param name="names">A collection of network interface names.</param>
		public void AddNetworkInterfaceNames( NetworkInterfaceNameCollection names )
		{
			NetworkInterfaceComboBox.Items.Clear( );
			
			foreach( NetworkInterfaceName Name in names )
			{
				NetworkInterfaceComboBox.Items.Add( Name.Description );
			}

			// Select the first one in the list.

			if( NetworkInterfaceComboBox.Items.Count > 0 )
			{
				NetworkInterfaceComboBox.SelectedIndex = 0;
			}
			
			m_NetworkInterfaceNames = names;
		}

		/// <summary>
		/// Gets or sets the selected network interface.
		/// </summary>
		/// <remarks><para>Setting the network interface can only be
		/// done after the selection of network interface strings
		/// have been given to the this class via 
		/// <see cref="SettingsForm.AddNetworkInterfaceNames"/></para>
		/// <para>The string returned here is the network interface
		/// name not the user friendly description.</para></remarks>
		public string SelectedNetworkInterface
		{
			[ DebuggerStepThrough ]
			get
			{
				if( NetworkInterfaceComboBox.Items.Count > 0 )
				{
					return m_NetworkInterfaceNames[ NetworkInterfaceComboBox.SelectedIndex ].Name;
				}

				return String.Empty;
			}

			set
			{
				if( NetworkInterfaceComboBox.Items.Count > 0 )
				{
					for( int i = 0; i < m_NetworkInterfaceNames.Count; ++i )
					{
						NetworkInterfaceName Name = m_NetworkInterfaceNames[ i ];
							
						if( Name.Name == value )
						{
							NetworkInterfaceComboBox.SelectedIndex = i;
						}
					}
				}
			}
		}

		/// <summary>
		/// Raises the GetNetworkInterfaces event.
		/// </summary>
		protected void OnGetNetworkInterfaces( )
		{
			if( GetNetworkInterfaces != null )
			{
				GetNetworkInterfaces( this, new EventArgs( ) );
			}
		}
		
		/// <summary>
		/// Closes the form after the OK button was clicked.
		/// </summary>
		/// <param name="sender">The object that triggered the event.</param>
		/// <param name="e">The event arguments.</param>
		private void OKFormButtonClick( object sender, EventArgs e )
		{
			Close( );
		}

		/// <summary>
		/// Closes the form after the cancel button was clicked.
		/// </summary>
		/// <param name="sender">The object that triggered the event.</param>
		/// <param name="e">The event arguments.</param>
		private void CancelFormButtonClick( object sender, EventArgs e )
		{
			Close( );
		}

		/// <summary>
		/// Disables the Use Proxy Server radio button after the
		/// user has selected the "Use WinPcap" radio button.
		/// </summary>
		/// <param name="sender">The object that triggered the event.</param>
		/// <param name="e">The event arguments.</param>
		private void UseWinPcapRadioButtonCheckedChanged( object sender, EventArgs e )
		{
			if( m_ProcessEvents )
			{
				RadioButton RadioButton = ( RadioButton )sender;
	
				UseWinPcap = RadioButton.Checked;
	
				ValidateRemoteServerIPAddressForPacketCapture( );
				ValidateListeningPort( );
				
				// Get the available network interfaces.
				
				if( UseWinPcap )
				{
					OnGetNetworkInterfaces( );
				}
			}
		}

		/// <summary>
		/// Disables the Use Proxy Server radio button after the
		/// user has selected the "Use Raw Sockets" radio button.
		/// </summary>
		/// <param name="sender">The object that triggered the event.</param>
		/// <param name="e">The event arguments.</param>
		private void UseRawSocketsRadioButtonCheckedChanged( object sender, EventArgs e )
		{
			if( m_ProcessEvents )
			{
				RadioButton RadioButton = ( RadioButton )sender;
	
				UseRawSockets = RadioButton.Checked;
	
				ValidateRemoteServerIPAddressForPacketCapture( );
				ValidateListeningPort( );
				
				// Get the available network interfaces.
				
				if( UseRawSockets )
				{
					OnGetNetworkInterfaces( );
				}
			}
		}		
		
		/// <summary>
		/// Disables the Use Packet Capture radio button after the
		/// user has selected the Use proxy server radio button.
		/// </summary>
		/// <param name="sender">The object that triggered the event.</param>
		/// <param name="e">The event arguments.</param>
		private void UseProxyServerRadioButtonCheckedChanged( object sender, EventArgs e )
		{
			if( m_ProcessEvents )
			{
				RadioButton RadioButton = ( RadioButton )sender;
	
				UseProxyServer = RadioButton.Checked;
	
				ValidateListeningPort( );
				ValidateRemoteServerIPAddressForPacketCapture( );
			}
		}

		/// <summary>
		/// Checks that the IP address is not localhost or 127.0.0.1
		/// if packet capture is selected since winpcap does not work
		/// on the local machine.
		/// </summary>
		private void ValidateRemoteServerIPAddressForPacketCapture( )
		{
			string IPAddress = RemoteServerIPAddress;

			if( UseWinPcap || UseRawSockets )
			{
				if( IsLocalHost( IPAddress ) )
				{
					ErrorProvider.SetError( RemoteServerTextBox, 
						"Cannot capture packets sent to a server on the local machine." );
				}
				else
				{
					ErrorProvider.SetError( RemoteServerTextBox, "" );
				}
			}
			else
			{
				// Clear the error since we are not using packet capture
				// so any packet capture errors are irrelevant.

				ErrorProvider.SetError( RemoteServerTextBox, "" );
			}
		}

		/// <summary>
		/// Determines whether the IP address refers to the local host.
		/// </summary>
		/// <param name="address">The IP address string to check.</param>
		/// <returns>A status flag indicating whether the ip address
		/// refers to the local machine</returns>
		/// <remarks>This function does not do a Dns.Resolve to 
		/// check whether the address given is refers to the local machine
		/// since this takes far too long.  Instead the function just
		/// does a simple check for "localhost" and "127.0.0.1".</remarks>
		private static bool IsLocalHost( string address )
		{
			bool IsLocalHost = false;

			address = address.Trim( );

			if( ( "127.0.0.1" == address ) ||
			   ( String.Compare( "localhost", address, true, CultureInfo.InvariantCulture ) == 0 ) )
			{
				IsLocalHost = true;
			}

			return IsLocalHost;
		}

		/// <summary>
		/// Validates the remote server ip address.
		/// </summary>
		/// <param name="sender">The object that triggered this event.</param>
		/// <param name="e">The cancel event argument</param>
		private void RemoteServerTextBoxValidating( object sender, CancelEventArgs e )
		{
			if( UseWinPcap || UseRawSockets )
			{
				ValidateRemoteServerIPAddressForPacketCapture( );	
			}
		}

		/// <summary>
		/// Validates the proxy server listening port.
		/// </summary>
		/// <param name="sender">The object that triggered this event.</param>
		/// <param name="e">The cancel event argument</param>
		private void ListeningPortTextBoxValidating( object sender, CancelEventArgs e )
		{
			ValidateListeningPort( );
		}

		/// <summary>
		/// Validates the remote server's port.
		/// </summary>
		/// <param name="sender">The object that triggered this event.</param>
		/// <param name="e">The cancel event argument</param>
		private void RemoteServerPortTextBoxValidating( object sender, CancelEventArgs e )
		{
			ValidateRemoteServerPort( );		
		}

		/// <summary>
		/// Checks whether the specified string can be converted into
		/// a valid IP address port.
		/// </summary>
		/// <param name="portText">The port specified as a string.</param>
		/// <returns>A flag indicating whether the port is valid.</returns>
		private static bool IsValidPort( string portText )
		{
			bool IsValid = false;

			try
			{
				Convert.ToInt32( portText, 10 );
				IsValid = true;
			}
			catch( ArgumentOutOfRangeException )
			{
				// Number too big to fit in an int.
			}
			catch( FormatException )
			{
				// The number contains non-numeric characters.
			}
			catch( OverflowException )
			{
				// Number is too big to fit in an int.
			}

			return IsValid;
		}

		/// <summary>
		/// Validates the proxy server's listening port.
		/// </summary>
		private void ValidateListeningPort( )
		{
			if( UseProxyServer )
			{
				if( IsValidPort( ListeningPortTextBox.Text ) )
				{
					ErrorProvider.SetError( ListeningPortTextBox, "" );
				}
				else
				{
					ErrorProvider.SetError( ListeningPortTextBox, "Invalid proxy server listening port." );
				}
			}
			else
			{
				// Clear the error since we are not using the 
				// proxy server so the invalid port error is irrelevant.

				ErrorProvider.SetError( ListeningPortTextBox, "" );
			}
		}

		/// <summary>
		/// Validates the remote server's port.
		/// </summary>
		private void ValidateRemoteServerPort( )
		{
			if( IsValidPort( RemoteServerPortTextBox.Text ) )
			{
				ErrorProvider.SetError( RemoteServerPortTextBox, "" );
			}
			else
			{
				ErrorProvider.SetError( RemoteServerPortTextBox, "Invalid remote server port." );
			}
		}
	}
}
